/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.rie06.openvoip.client;

import com.rie06.openvoip.User;
import com.rie06.openvoip.client.IHM.communication.WindowDialog;
import com.rie06.openvoip.server.ServerMessage_Conversation;
import java.net.BindException;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.*;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

/**
 * Cette classe permet de créer une conversation. Chaque conversation dispose d'un UUid afin d'être identifier
 * 
 * Cette classe permet également de rajouter un client ou un contact à une conversation déjà en cours.
 */


public class Conversation implements Runnable {

    private static final Logger logger = Logger.getLogger(Client.class);
    private Client client;
    private UUID uuid;
    private Map contactList;
    private Map contactListCall;
    private boolean master;
    private UUID masterContactUUID;
    private Map conversationPlayer;
    private ConversationRecorder conversationRecorder;
    private DatagramSocket socket;
    private Thread thread;

    public Conversation(Client client, UUID uuid, Contact contact, UUID masterContactUUID) {
        Initialization(client, uuid);
        this.master = false;
        this.masterContactUUID = masterContactUUID;
        this.contactList.put(contact.getUuid(), contact);
        conversationPlayer.put(contact.getUuid(), new ConversationPlayer(this));
        Thread thread = null;
        logger.log(Level.DEBUG, "New conversation request by <" + contact.getName() + "> uuid: " + uuid);
    }

    public Conversation(Client client, UUID uuid) {
        Initialization(client, uuid);
        this.master = true;
        this.masterContactUUID = client.getServer().getServerConfig().getId();

        Thread thread = new Thread(this);
        thread.setName("Conversation");
        thread.start();
        logger.log(Level.DEBUG, "Conversation Contact List Size: " + contactList.size());
    }

    private void Initialization(Client client, UUID uuid) {

        this.client = client;
        this.contactList = new HashMap();
        this.contactListCall = new HashMap();
        this.conversationPlayer = new HashMap();

        this.uuid = uuid;

        Random r = new Random();
        int port = 0;
        boolean validate = false;
        while (!validate) {
            try {
                port = 1997 + r.nextInt(25);
                this.socket = new DatagramSocket(port);
                validate = true;
            } catch (BindException ex) {
                logger.log(Level.INFO, "Port: " + port + " - Already taked, find other port to bind UDP Out.");
            } catch (SocketException ex) {
                java.util.logging.Logger.getLogger(Conversation.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            }
        }

        this.conversationRecorder = new ConversationRecorder(this);
    }

    public void addContact(Contact contact) {
        logger.log(Level.DEBUG, "New conversation with <" + contact.getName() + "> uuid: " + uuid + " - You are the Master.");

        ServerMessage_Conversation smc = new ServerMessage_Conversation("conversation", 0, this.uuid, client.getServer().getServerConfig().getId());
        User u = (User) client.getServer().getUsersList().get(contact.getUuid());
        u.getServerTCPThread().sendMessage(smc);
        contactList.put(contact.getUuid(), contact);
        conversationPlayer.put(contact.getUuid(), new ConversationPlayer(this));
        sendContactList();
    }

    public void sendMessage(String text) {
        HashMap m = (HashMap) contactList;
        for (Iterator it = m.entrySet().iterator(); it.hasNext();) {
            Map.Entry<UUID, Contact> entry = (Map.Entry<UUID, Contact>) it.next();

            UUID uuid = entry.getKey();
            Contact contact = entry.getValue();

            ServerMessage_Conversation smc = new ServerMessage_Conversation("conversation", 4, this.uuid, client.getServer().getServerConfig().getId(), text);
            User u = (User) client.getServer().getUsersList().get(contact.getUuid());
            u.getServerTCPThread().sendMessage(smc);
        }
    }

    public void receiveMessage(String text) {
        WindowDialog wd = (WindowDialog) client.getIhm().getConversationWindowList().get(uuid);
        wd.getPanelMessage().getZoneConversation().setText(wd.getPanelMessage().getZoneConversation().getText() + text);
        wd.getPanelMessage().getZoneConversation().repaint();
    }

    public void sendContactList() {
        HashMap m = (HashMap) contactList;
        for (Iterator it = m.entrySet().iterator(); it.hasNext();) {
            Map.Entry<UUID, Contact> entry = (Map.Entry<UUID, Contact>) it.next();

            UUID uuid = entry.getKey();
            Contact contact = entry.getValue();

            ServerMessage_Conversation smc = new ServerMessage_Conversation("conversation", 1, this.uuid, client.getServer().getServerConfig().getId(), this.contactList, this.contactListCall, conversationRecorder.isPause());
            User u = (User) client.getServer().getUsersList().get(contact.getUuid());
            u.getServerTCPThread().sendMessage(smc);
        }
    }

    public void updateContactList(Map contactList, Map contactListCall, boolean masterIsPause) {
        HashMap m = (HashMap) contactList;
        UUID myUUID = client.getServer().getServerConfig().getId();
        for (Iterator it = m.entrySet().iterator(); it.hasNext();) {
            Map.Entry<UUID, Contact> entry = (Map.Entry<UUID, Contact>) it.next();

            UUID uuid = entry.getKey();
            Contact contact = entry.getValue();

            if (!client.getContactList().containsKey(uuid) && !myUUID.equals(uuid)) {
                client.addContactToList(uuid, contact.getName(), contact.getIpAddress(), contact.getPort());
            }

            if (!this.contactList.containsKey(uuid) && !myUUID.equals(uuid)) {
                this.contactList.put(uuid, contact);
            }
        }
        m = (HashMap) contactListCall;
        for (Iterator it = m.entrySet().iterator(); it.hasNext();) {
            Map.Entry<UUID, Contact> entry = (Map.Entry<UUID, Contact>) it.next();

            UUID uuid = entry.getKey();
            Contact contact = entry.getValue();

            if (!this.contactListCall.containsKey(uuid) && !myUUID.equals(uuid)) {
                this.contactListCall.put(uuid, contact);
            }
        }
        if (!masterIsPause) {
            if (!this.contactListCall.containsKey(masterContactUUID)) {
                this.contactListCall.put(masterContactUUID, contactList.get(masterContactUUID));
            }
        }

//        if (client.getIhm().getConversationWindowList().containsKey(uuid)) {
            WindowDialog wd = (WindowDialog) client.getIhm().getConversationWindowList().get(uuid);
            wd.getPanelDialog().update();
//        }
    }

    public void play() {
        if (client.getCurrentConversation() == null || client.getCurrentConversation().equals(this.uuid)) {

            HashMap mp = (HashMap) conversationPlayer;
            for (Iterator it = mp.entrySet().iterator(); it.hasNext();) {
                Map.Entry<UUID, ConversationPlayer> entry = (Map.Entry<UUID, ConversationPlayer>) it.next();

                UUID uuid = entry.getKey();
                ConversationPlayer player = entry.getValue();

                synchronized (player.getThread()) {
                    player.setPause(false);
                    player.getThread().notify();
                }

            }

            synchronized (conversationRecorder.getThread()) {
                conversationRecorder.setPause(false);
                conversationRecorder.getThread().notify();
            }

            //send play to other Client
            HashMap m = (HashMap) contactList;
            for (Iterator it = m.entrySet().iterator(); it.hasNext();) {
                Map.Entry<UUID, Contact> entry = (Map.Entry<UUID, Contact>) it.next();

                UUID uuid = entry.getKey();
                Contact contact = entry.getValue();

                ServerMessage_Conversation smc = new ServerMessage_Conversation("conversation", 3, this.uuid, client.getServer().getServerConfig().getId());
                User u = (User) client.getServer().getUsersList().get(contact.getUuid());
                u.getServerTCPThread().sendMessage(smc);
            }
         client.setCurrentConversation(this.uuid);   
        }
    }

    public void setPlay(UUID contactUUID) {
        //set user in play list
        Contact c = (Contact) contactList.get(contactUUID);
        contactListCall.put(c.getUuid(), c);
        WindowDialog wd = (WindowDialog) client.getIhm().getConversationWindowList().get(uuid);
        wd.getPanelDialog().update();
    }

    public void pause() {
        if (client.getCurrentConversation() == null || client.getCurrentConversation().equals(this.uuid)) {

            HashMap mp = (HashMap) conversationPlayer;
            for (Iterator it = mp.entrySet().iterator(); it.hasNext();) {
                Map.Entry<UUID, ConversationPlayer> entry = (Map.Entry<UUID, ConversationPlayer>) it.next();

                UUID uuid = entry.getKey();
                ConversationPlayer player = entry.getValue();

                synchronized (player.getThread()) {
                    player.setPause(true);
                    player.getThread().notify();
                }
            }


            synchronized (conversationRecorder.getThread()) {
                conversationRecorder.setPause(true);
                conversationRecorder.getThread().notify();
            }

            //send pause to other Client
            //send play to other Client
            HashMap m = (HashMap) contactList;
            for (Iterator it = m.entrySet().iterator(); it.hasNext();) {
                Map.Entry<UUID, Contact> entry = (Map.Entry<UUID, Contact>) it.next();

                UUID uuid = entry.getKey();
                Contact contact = entry.getValue();

                ServerMessage_Conversation smc = new ServerMessage_Conversation("conversation", -3, this.uuid, client.getServer().getServerConfig().getId());
                User u = (User) client.getServer().getUsersList().get(contact.getUuid());
                u.getServerTCPThread().sendMessage(smc);
            }

            client.setCurrentConversation(null);
        }
    }

    public void setPause(UUID contactUUID) {
        //set user in user list
        Contact c = (Contact) contactList.get(contactUUID);
        contactListCall.remove(c.getUuid());
        WindowDialog wd = (WindowDialog) client.getIhm().getConversationWindowList().get(uuid);
        wd.getPanelDialog().update();
    }

    public void removeContactFromConversation(Contact contact) {
        contactList.remove(contact.getUuid());
        contactListCall.remove(contact.getUuid());
        conversationPlayer.remove(contact.getUuid());

        //send disconnect to contact
        ServerMessage_Conversation smc = new ServerMessage_Conversation("conversation", -2, this.uuid, client.getServer().getServerConfig().getId());
        User u = (User) client.getServer().getUsersList().get(contact.getUuid());
        u.getServerTCPThread().sendMessage(smc);

        HashMap m = (HashMap) contactList;
        for (Iterator it = m.entrySet().iterator(); it.hasNext();) {
            Map.Entry<UUID, Contact> entry = (Map.Entry<UUID, Contact>) it.next();

            UUID uuid = entry.getKey();
            Contact c = entry.getValue();

            smc = new ServerMessage_Conversation("conversation", -2, this.uuid, contact.getUuid());
            u = (User) client.getServer().getUsersList().get(contact.getUuid());
            u.getServerTCPThread().sendMessage(smc);
        }
    }

    public void closeConversation() {
        HashMap m = (HashMap) contactList;
        for (Iterator it = m.entrySet().iterator(); it.hasNext();) {
            Map.Entry<UUID, Contact> entry = (Map.Entry<UUID, Contact>) it.next();

            UUID uuid = entry.getKey();
            Contact contact = entry.getValue();

            ServerMessage_Conversation smc = new ServerMessage_Conversation("conversation", -2, this.uuid, client.getServer().getServerConfig().getId());
            User u = (User) client.getServer().getUsersList().get(contact.getUuid());
            u.getServerTCPThread().sendMessage(smc);
        }
        WindowDialog wd = (WindowDialog) client.getIhm().getConversationWindowList().get(this.uuid);
        wd.setVisible(false);
        wd = null;
        thread = null;
        client.closeConversation(this.uuid);
    }

    public void setCloseConversation(UUID uuid) {
        //if uuid is master -> Close conversation
        if (masterContactUUID.equals(uuid)) {
            WindowDialog wd = (WindowDialog) client.getIhm().getConversationWindowList().get(this.uuid);
            wd.setVisible(false);
            wd = null;
            thread = null;
            client.closeConversation(this.uuid);
        } else {
            //if not, delete the user
            conversationPlayer.remove(uuid);
            contactList.remove(uuid);
            contactListCall.remove(uuid);
            WindowDialog wd = (WindowDialog) client.getIhm().getConversationWindowList().get(this.uuid);
            wd.getPanelDialog().update();
        }
    }

    public void playSound(UUID userID, byte[] payload_voice) {
        //Verify if need to play this conversation
        if (client.getCurrentConversation().equals(this.uuid) && conversationPlayer.containsKey(userID) && contactListCall.containsKey(userID) && !conversationRecorder.isPause()) {
            ConversationPlayer player = (ConversationPlayer) conversationPlayer.get(userID);
            player.getSound().add(payload_voice);
        }
    }

    public Client getClient() {
        return client;
    }

    public Map getContactList() {
        return contactList;
    }

    public Map getContactListCall() {
        return contactListCall;
    }

    public void setContactList(Map contactList) {
        this.contactList = contactList;
    }

    public UUID getUuid() {
        return uuid;
    }

    public boolean isMaster() {
        return master;
    }

    public void setMaster(boolean master) {
        this.master = master;
    }

    public UUID getMasterContactUUID() {
        return masterContactUUID;
    }

    public void setMasterContactUUID(UUID masterContactUUID) {
        this.masterContactUUID = masterContactUUID;
    }

    public DatagramSocket getSocket() {
        return socket;
    }

    public Map getConversationPlayer() {
        return conversationPlayer;
    }

    public ConversationRecorder getConversationRecorder() {
        return conversationRecorder;
    }

    public void run() {
        while(thread!=null){
            try {
                this.sendContactList();
                thread.sleep(1000);
            } catch (InterruptedException ex) {
                java.util.logging.Logger.getLogger(Conversation.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            }
        }
    }
}
